home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Apple II Magazines (PO)
/
Nibble Volume 10, No. 07 (1989-07)(MindCraft Publishing)(Side A).zip
/
Nibble Volume 10, No. 07 (1989-07)(MindCraft Publishing)(Side A).po
/
SUPERCLOCK.S
< prev
next >
Wrap
Text File
|
1996-12-24
|
26KB
|
643 lines
******************************************************
* SUPERCLOCK *
* By Peter Stubbs *
* Copyright(c) 1989 *
* MindCraft Publ. Corp. *
* Concord, Ma 01742 *
* MERLIN 816 ASSEMBLER *
******************************************************
* Will be permanently installed during ProDOS 16 boot
* if placed in the SYSTEM/SYSTEM.SETUP subdirectory
* of your ProDOS 16 boot disk. This occurs because
* we make the file of type $B6 (permanent initialisation
* file). Program will be made fixed and non-purgable.
REL ;Tell Merlin - relocatable file
USE CLOCK.MACS ;Our macro file name
DSK CLOCK.L ;assemble link file under this name
* Equates
BUSYFLG EQU $E100FF ;0 = not busy, 1 = busy
INCBUSYFLG EQU $E10064
DECBUSYFLG EQU $E10068
OS_KIND EQU $E100BC ;0 = ProDOS 8, 1 = ProDOS 16
MX %00 ;Tel Merlin we're in 16 bits
PHB ;Save data bank
PHK ;Move program bank
PLB ; to data bank register
* Zero our buffer and set up our flags and masks
LDX #$00D2 ;Clear our flags etc
LDA #0000 ; to zero
Loop STA TimeBuff,X
DEX
DEX
BPL Loop ;Until all are done
* Set up Flush Buffer vector to point at our handler
PEA 0000 ;Space for result
PEA 0000 ;Space for result
PushWord #$0013 ;Flush Buffer
_GetVector ;Ask for current vector
PLA ;Get its address (low byte)
STA OldFlush+1 ; and insert into our code
PLA ;Get address (high byte)
ShortA ;8 bit acc. so we only store
STA OldFlush+3 ; the low byte in our code
FullA ;Back to full 16 bits
PushWord #$0013 ;Flush Buffer
PushPtr OurFlush ;Replace current Flush Buffer vector
_SetVector ; with a pointer to our reinstall code
* Set up BELLVECTOR to point at our handler
PEA 0000 ;Space for result
PEA 0000 ;Space for result
PushWord #$001B ;BELLVECTOR
_GetVector ;Ask for current vector
PLA ;Get its address (low byte)
STA OldBell+1 ; and insert into our code
PLA ;Get address (high byte)
ShortA ;8 bit acc. so we only store
STA OldBell+3 ; the low byte in our code
FullA ;Back to full 16 bits
PushWord #$001B ;BELLVECTOR
PushPtr OurBell ;Replace current BELLVECTOR with
_SetVector ; a pointer to our reinstall code
PLB ;Restore data bank
* This code installs the clock code into the Heartbeat Interrupt
* Task queue when first run and when Option-Control-G is pressed
* or when Flush Buffer (Control-Open-Apple-Delete) is pressed.
Install PHB ;Save data bank
PHK ;Move program bank
PLB ; to data bank register
LDA #$0001 ;Set tick counter to #$1
STA Timer ; to force an update
PushPtr HrtStart
_SetHeartBeat ;install heartbeat
ShortA ;8 bit accumulator
LDA #%00001000
ORAL $E1C041 ;set VBL enable bit
STAL $E1C041
STZ OnFlg ;so we know time is on (1=off)
FullA ;16 bit accumulator
PLB ;Restore data bank
RTL ;Exit
* The code below is vectored to when the user issues a flush
* by typing Control-Open Apple-Delete. This is one way of
* reinstalling SUPERCLOCK after you have removed it, but most
* ProDOS 16 applications temporarily disable this vector. To
* reinstall SUPERCLOCK from such applications a second piece
* of code is activated by Option-Control-G. (see below)
OurFlush FullAXY ;16 bit A,X,Y
JSL Install ;Registers set up, now reinstall heartbeat
ShortAXY ;8 bit A,X,Y
OldFlush JML $FFB5DE ;Exit via original flush handler
* The code below is vectored to when a bell character is output.
* As typing Control-G will output a bell character, this
* gives us an alternative way of reinstalling SUPERCLOCK.
* For cases where Control-Open Apple-Delete does not
* reinstall SUPERCLOCK (e.g. the Finder) enter the control
* panel by pressing Control-Open Apple-Esc and then press
* Option-Control-G to reinstall SUPERCLOCK. To exit
* the control panel you select QUIT and hit return.
OurBell LDAL $E1C025 ;Get KEYMODEREG to check bit
ASL ;Move bit 5 (pos'n of Option key) left
ASL ; and then into carry
BCC NotOurs ;Bit 5 = 0, so Option key not down
STAL $E1C010 ;Clear keyboard strobe to discard Ctrl-G
PHX ;Save X
PHB ;Save data bank
PHK ;Move program bank
PLB ; to data bank register
FullAXY ;16 bit A,X,Y
JSL Install ;Go re-install SUPERCLOCK
ShortAXY ;8 bit A,X,Y
PLB ;Restore bank
PLX ;Restore X
LDY #$0 ;Y = 0 as required by Apple protocols
SEC ;So bell is sounded
RTL
NotOurs SEC ;So we get a bell
OldBell JML $FFB5DE ; exit via original interrupt handler
HrtStart ADRL $0000 ;Space for task pointer
Timer DA $001E ;no. of ticks = 30 ($1E) i.e. every 1/2 sec
HEX 5AA5 ;heartbeat signature word
FullAXY ;16 bit A,X,Y
PHB ;Save data bank
PHK ;Move program bank
PLB ; to data bank register
* We will use _ReadTimeHex to get day, date and time
* rather than _ReadAsciiTime to avoid problems with
* some AppleWorks enhancement programs like ULTRAMACROS
* that "die" when _ReadAsciiTime is used. _Int2Dec is
* then used to turn the hex values into ASCII strings.
GetTime PEA 0000 ;Make space for result
PEA 0000
PEA 0000
PEA 0000
_ReadTimeHex ;Get time in hex
LDA #$2020 ;Put inverse spaces between text
STA TimeBuff+19
LDA #$203A ;": "
STA TimeBuff+16
PLA ;Get MINUTE|SECOND
XBA ;Swap Minutes to low byte
AND #%0000000011111111 ;remove seconds
PHA ;Mins on stack for next tool call
PushPtr TimeBuff+17 ;Point at string output
PEA 0002 ;2 digits in string
PEA 0000 ;unsigned
_Int2Dec ;Make MINS in hex into ASCII string
LDA TimeBuff+16 ;Get result
CMP #$203A ;Is it : and space
BNE OK ;No, so must be 2 digits
LDA #$303A ;Yes, so add leading zero
STA TimeBuff+16
OK PLA ;Get YEAR|HOUR
PHA ;Save for later
LDX #$0D01 ;AM text
AND #%0000000011111111 ;Strip out year
BNE NOT0 ;Branch if acc <> 0
LDA #$000C ;So 0 hours becomes 12 AM
BRA AM
NOT0 CMP #$000C ;12 or less?
BEQ PM ;12 hours = 12 PM
BLT AM ;1-11 hours = AM
SEC
SBC #$000C ;Adjust to 12 hr clock
PM LDX #$0D10 ;PM text
AM STX TimeBuff+20 ;Store AM or PM text
PHA ;HOUR on stack for next tool call
PushPtr TimeBuff+13 ;Point at string output
PEA 0003 ;3 digits (gives leading space)
PEA 0000 ;unsigned
_Int2Dec ;Make HOUR in hex into ASCII string
PLA ;Get YEAR|HOUR back
XBA ;Swap high & low bytes to get HOUR|YEAR
AND #%0000000011111111 ;and mask out HOUR
PHA ;YEAR on stack for next tool call
PushPtr TimeBuff+11 ;Point at string output
PEA 0002 ;2 digits in string
PEA 0000 ;unsigned
_Int2Dec ;Make YEAR in hex into ASCII string
PLA ;Get MONTH|DAY
PHA ;Save for later
AND #%0000000011111111 ;Discard all but day
INC ;Turn 0-30 into 1-31
PHA ;DAY on stack for next tool call
PushPtr TimeBuff+4 ;Point at string output
PEA 0002 ;2 digits in string
PEA 0000 ;unsigned
_Int2Dec ;Make DAY in hex into ASCII string
LDA #$202D ;Put - and space into buffer
STA TimeBuff+6
PLA ;Get MONTH|DAY back
XBA ;Swap bytes so have DAY|MONTH
AND #%0000000011111111 ;Discard all but MONTH
ASL ;Multiply
ASL ; by 4 for table offset
TAX ;Move to index
LDA TBL,X ; and put the month text
STA TimeBuff+7 ; into our buffer
LDA TBL+2,X
STA TimeBuff+9
PLA ;Get WEEKDAY|null
XBA ;Swap bytes so have null|WEEKDAY
DEC ;Change 1-7 to 0-6
ASL ; and multiply
ASL ; by 4 for table offset
TAX ;Move to index
LDA TBL2,X ; and put day text
STA TimeBuff ; into our buffer
LDA TBL2+2,X
STA TimeBuff+2
STZ Mode ;Initialise our flags with 00
STZ EFlag
ShortA ;8 bit accumulator
LDAL OS_KIND ;ProDos 16 active?
BEQ Not16 ;No so use text display
LDAL $E1C029 ;Is Super Res display on ?
AND #%10000000 ;Mask out all but Super Res bit
BEQ Not16 ;No, so use text display
INC Mode ;Mode = 1 means Super Res display
Not16 FullAXY ;16 bit A,X,Y
LDA Mode ;Get display type
BEQ Cont ;Prodos 8 active or Super Res off
PHA ;Space for result
_QDStatus ;Is QuickDraw active ?
PLA ;Get result
BNE Cont ;Branch if yes, all is OK for Super Res
STZ PortFlag ;No, so clear flag
BRA ToExit ; and exit
Cont LDAL BUSYFLG ;check busyflag
BEQ Cont2 ;Non zero so proceed
ToExit BRL Exit ;else exit
Cont2 JSL INCBUSYFLG ;Set busyflag so we're not interrupted
* We have some commands that need to be checked for:-
*
* OPTION-D = Date display on/off (default is ON)
* OPTION-W = Day of week display on/off (default is ON)
* OPTION-F = Flip display position TOP/BOTTOM (default is TOP)
* OPTION-I = Inverse display on/off (default is ON)
* OPTION-O = Turn display on/off (default is ON)
* OPTION-R = Remove program from heartbeat queue
*
ShortAXY ;8 bit A,X,Y
LDY #$0 ;Make X and Y
TYX ; both 00
Loop0 LDAL $E1C025 ;Get KEYMODEREG to check bit
ASL ; 6 for OPTION key pressed
ASL
BCS Hit ;Bit 6 now in carry, branch if key down
ToNot JMP Not ; else exit keyboard test code
* Option key is down. To let user know we're reading it we
* click the speaker. To achieve this we toggle the speaker
* using a delay loop in X and Y.
Hit INY ;Advance first delay counter
INY ; by two
BNE NoBeep ;Only fall through every 128 times
INX ;Advance second delay counter
INX ; by two
BNE NoBeep ;Only fall through every 128 times
STAL $E0C030 ;Now we're here, toggle speaker
NoBeep LDAL $E1C000 ;Read keyboard
BPL Loop0 ; no key is down
CMP #$E0 ;Else check for lower case
BLT NotLow
AND #$DF ;force to upper case
NotLow CMP #"F" ;Is it F(lip) display position
BEQ Flip ;YES
CMP #"R" ;Is it R(emove) heartbeat
BEQ Remove ;YES
CMP #"I" ;Is it I(nverse) on/off
BEQ Inverse ;YES
CMP #"W" ;Is it day of W(eek) on/off
BEQ Day ;YES
CMP #"D" ;Is it D(ate) on/off
BEQ Date ;YES
CMP #"O" ;Is it O(ff) / O(n)
BNE ToNot ;NO, so exit keyboard test
LDA OnFlg ;Get flag
EOR #$00000001 ;Reverse bit 0 (Toggles ON/OFF)
GoClear STA OnFlg ; and update the display flag
STA EFlag ; and the erase flag
JSR ClearIt ;Clear current date/time
JMP CmdDone ; and exit
ClearIt LDA Patch1+1 ;Find current date/time location
CMP #$1D ;will be #$1D if at top of screen
BNE Bottom
JMP ClearTop ;Clear date/time from top and return
Bottom JMP ClearBtm ;Clear date/time from bottom and return
Flip JSR ClearIt ;Clear date/time from current position
FullAXY ;16 bit A,X,Y
LDA Patch1+1 ;Modify the date/time positioning code
EOR #$3F0 ; to flip display between top and
STA Patch1+1 ; bottom screen lines on text screen
STA Patch2+1 ; so next time we update the time it
LDA Patch3+1 ; is in the other position
EOR #$3F0
STA Patch3+1
ShortAXY ;8 bit A,X,Y
BRA CmdDone
Remove FullAXY ;16 bit A,X,Y
PushPtr HrtStart
_DelHeartBeat ;Remove from heartbeat interrupt queue
ShortAXY ;8 bit A,X,Y
LDA #$1 ;So we set the off and erase flags
BRA GoClear ;Now go clear date/time from screen
Inverse LDA OrMask ;Get current mask
EOR #%10000000 ;Reverse setting of bit 7
STA OrMask ; and save it back
BRA CmdDone
Day JSR ClearIt ;Clear current display
LDA Mode ;Get display mode
STA EFlag ;Makes EFlag 1 for P16 so we
LDA WFlag ; erase the old date format
EOR #%00000001 ;Reverse the setting of the
STA WFlag ; weekday flag and save it back
BRA CmdDone ;All done
Date JSR ClearIt ;Clear current display
LDA Mode ;Get display mode
STA EFlag ;Makes EFlag 1 for P16 so we
LDA DFlag ; erase the old date format
EOR #%00000001 ;Reverse the setting of the date
STA DFlag ; flag and save it back
CmdDone STAL $E1C010 ;Clear the keyboard strobe
Not FullAXY ;16 bit A,X,Y
LDA Mode ;Get current mode
BNE P16 ;Prodos 16 and Quickdraw both active
JMP P8 ;Else go to text display code
P16 PHA
PHA ;Space for result
_GetPort ;Get pointer to current Grafport
LDA PortFlag ;Port Initialised?
BNE InitDone
* Set up our port and restrict region
PushPtr Port
_InitPort ;Like OpenPort, but assumes handles OK
LDA #$0002 ;top of rectangle is screen line 2
STA PortRect
LDA #$000B ;bottom of rectangle is screen line 11
STA PortRect+4
LDA #<ClipHndl ;Set up the clip and visible regions
STA ClipRgn ; in our Grafport to point at ClipHndl
STA VisRgn ; so that our handles are OK (better
LDA #^ClipHndl ; than letting QuickDraw do it as it
STA ClipRgn+2 ; takes less space and our handles
STA VisRgn+2 ; are not lost by any ClosePort commands)
INC PortFlag ;So we know we've initialised the GrafPort
InitDone PHA ;Space for result
_GetMasterSCB ;Find out if we're in 640 or 320 mode
PLA ;Get result
STA PortInfo ;Information is also needed in GrafPort
BEQ Is320 ;00=320, 80=640
LDX #$01DA ;Set correct dtls for 640 mode
LDY #$027C ; left and right
BRA SetSides ;Go set the values
Is320 LDX #$009C ;Set correct dtls for 320 mode
LDY #$013E ; left and right
SetSides STX PortRect+2 ;Set left pixel of our GrafPort
STX ClipRect+2 ; and our clip rectangle
STY PortRect+6 ;Set right pixel of our GrafPort
STY ClipRect+6 ; and our clip rectangle
PushPtr Port
_SetPort ;Make our port the current one
LDA EFlag ;Get the port erase flag
BEQ NoErase ;00 = erase not needed
PushPtr PortRect
_EraseRect ;Fill with bgr pattern
NoErase LDA OnFlg ;Get display flag
BEQ Update ;00 = show date/time
BRL NoUpdate ; else exit update code
Update PHA ;Space for result of next tool call
ShortA ;8 bit accumulator
LDX #$0016 ;Clear High bit of any
Loop2 LDA TimeBuff,X ; letters in the buffer
CMP #$1B ;Is it a letter?
BGE NUM ;Branch if not
ORA #%01000000 ;Clear, so it prints OK
NUM STA TimeBuff,X
DEX
BPL Loop2 ;Until done
LDA DFlag ;Get date flag (0 = show date)
BNE JustTime ;Time only
LDA WFlag ;Get day of week flag (0=show)
BNE NoWeek
FullA ;16 bit accumulator
PushPtr Length ;Point at start of string (23 chars) if
BRA DoCalc ; date option is on
MX %11 ;Tell Merlin we're in 8 bits
NoWeek LDA #$12 ;No week, shorten string length to 18
STA TimeBuff+3 ; and save into buffer just before date
MX %00 ;Tell Merlin we're in 16 bits
FullA ;16 bit accumulator
PushPtr TimeBuff+3 ;Now point at our shorter string
BRA DoCalc
MX %11 ;Tell Merlin we're in 8 bits
JustTime LDA #$08 ;Just time, shorten string length to 8
STA TimeBuff+13 ; and save into buffer just before time
MX %00 ;Tell Merlin we're in 16 bits
FullA ;16 bit accumulator
PushPtr TimeBuff+13 ;Now point at our shorter time only string
DoCalc _StringWidth ;Get string width in pixels
PLA ;(so we can right justify our text)
STA Width ;and save it
LDA PortRect+6 ;Get right pixel of Port rectangle
SEC ; then subtract the pixel width of
SBC Width ; our string to get the left pixel
STA PortRect+2 ; and save that in Port rect definition
PHA ;Pos'n pen horiz.
LDA #$000A ;Get vertical pos
PHA
_MoveTo ;Move pen to drawing location
LDA DFlag ;Get date on flag (0 = show date)
BEQ WithDate ;If zero, go see if we show day also
PushPtr TimeBuff+13 ;Point at time only
BRA DrawIt
WithDate LDA WFlag ;Get day of week flag (0 = show day)
BEQ WithWeek ;If zero, go show it
PushPtr TimeBuff+3 ;Else point past it in buffer
BRA DrawIt ; and go print it
WithWeek PushPtr Length ;Point at full date/time text
DrawIt _DrawString ;Print text in buffer to screen
NoUpdate _SetPort ;original port ptr still on stack
Exit2 JSL DECBUSYFLG ;Clear busy flag as we're all done
Exit LDA #$001E ;Set the tick counter back to 30 (1/2 sec)
STA Timer
PLB ;Restore data bank
ShortAXY ;8 bit A,X,Y
RTL ;and exit
P8 ShortAXY ;8 bit A,X,Y
LDA OnFlg ;Get display flag (0 = on)
BEQ On
BRA Leave ;Else leave display code
* Before actually displaying the time on the text screen
* we need to determine whether we are in 40 or 80 column
* mode. We need to know this because we are writing
* directly to the screen RAM and the way this is used is
* different for the two modes. In 40 column mode the
* display RAM is from $E1400-$E17FF only, but in 80 column
* mode display RAM is $E1400-$E17FF interleaved with RAM
* from $E0400-$E07FF.
On LDY #$0
TYX ;X=0 also
LDAL $E1C01F ;Read 40/80 column switch
BPL Forty
LDA DFlag ;Get date flag (0 = on)
BEQ DateOn ;Branch if date and time wanted
LDY #$E ;Start at time text in buffer
LDX #$7 ;So we put time in correct RAM
BRA ELoop ;ALWAYS
DateOn LDA WFlag ;Get weekday flag (0 = on)
BEQ ELoop ;Branch if week on
LDY #$4
LDX #$2 ;Fall through and check date flag
ELoop LDA TimeBuff,Y ;Get character
ORA OrMask ;Convert to current display format
Patch1 STAL $E1041D,X ;Store in screen RAM
INY ;advance buffer pointer
CPY #$16 ;Are we done ?
BEQ Leave ;Yes, so exit
LDA TimeBuff,Y ;else get next character
ORA OrMask ;Convert to current display format
Patch2 STAL $E0041D,X ;and store in other RAM bank
INY ;advance buffer pointer
INX ;but not RAM pointer
CPY #$16 ;Are we done?
BEQ Leave ;Yes, so exit
BRA ELoop ;ALWAYS
Forty LDA DFlag ;Get date flag (0 = on)
BEQ WeekText ;Go see if we show weekday
LDY #$E ;Start at time text in buffer
TYX ;and set X to correct RAM offset
BNE FixText
WeekText LDA WFlag ;Get weekday flag (0 = show day)
BEQ FixText
LDY #$4
TYX
FixText LDA TimeBuff,Y ;Get character
ORA OrMask ;Convert to current display format
Patch3 STAL $E00412,X ;Store in screen RAM
INY ;Advance text pointer
INX ;and screen RAM pointer
CPY #$16 ;Are we done?
BNE FixText ;No, so loop back
Leave FullAXY ;16 bit A,X,Y
JMP Exit2 ;All done, so exit
MX %11 ;Tell Merlin we're in 8 bits
* Following code is used to erase time details from
* the text screen RAM when flipped or when the display
* format is otherwised changed. The starting value
* for X in the clear code depends on the display
* format and so this is determined before the clear
* code is executed.
ClearBtm LDAL $E1C01F ;Read 40/80 column switch
BPL IsForty ;Skip 80 col stuff
LDX #$24 ;Starting position for erase
LDA DFlag ;Get date flag (0 = on)
BNE ClrBtm2 ;Branch if date off
LDX #$1F ;Change starting pos'n
LDA WFlag ;Get weekday flag (0 = on)
BNE ClrBtm2 ;Branch if weekday off
LDX #$1D ;Change starting pos'n
BRA ClrBtm2 ;Go to start of clear code
IsForty LDX #$20 ;Starting position for erase
LDA DFlag ;Get date flag (0 = on)
BNE ClrBtm2 ;Branch if date off
LDX #$16 ;Starting position for erase
LDA WFlag ;Get Weekday flag (0 = on)
BNE ClrBtm2 ;Branch if weekday off
LDX #$12 ;If weekday on
ClrBtm2 LDA #$A0 ;Load acc. with a space
BLoop STAL $E107D0,X ;and modify screen RAM
STAL $E007D0,X
INX ;advance screen RAM pointer
CPX #$28 ;Are we done?
BNE BLoop ;No, so loop
RTS ;Else return to caller
* Similar code to the ClearBtm routine
ClearTop LDAL $E1C01F
BPL IsForty2
LDX #$24
LDA DFlag
BNE ClrTop2
LDX #$1F
LDA WFlag
BNE ClrTop2
LDX #$1D
BRA ClrTop2
IsForty2 LDX #$20
LDA DFlag
BNE ClrTop2
LDX #$16
LDA WFlag
BNE ClrTop2
LDX #$12
ClrTop2 LDA #$A0
TLoop STAL $E10400,X
STAL $E00400,X
INX
CPX #$28
BNE TLoop
RTS
TBL INV "JAN-FEB-MAR-APR-MAY-JUN-JUL-AUG-SEP-OCT-NOV-DEC-"
TBL2 INV "SUN MON TUE WED THU FRI SAT "
MX %00 ;Tell Merlin we're in 16 bits
Clip DA 10 ;This is part of the region definition
ClipRect DA 1,477,11,636 ;Top,Left,Bottom,Right of our rect
ClipHndl ADRL Clip ;Handle to the clip and visible regions
Length DB 23 ;Length of string that will hold date/time
ASC ' ' ;Include a leading space before date/time
TimeBuff EQU * ;Holds the date/time string
PortFlag EQU TimeBuff+22 ;0=Init done, 1=Not done
OnFlg EQU PortFlag+2 ;0=ON, 1=OFF
Mode EQU OnFlg+2 ;0=P8, 1=P16
EFlag EQU Mode+2 ;Non zero = erase needed
DFlag EQU EFlag+2 ;0=Date, 1=No Date
WFlag EQU DFlag+2 ;0=Day of week, 1=No Day of week
OrMask EQU WFlag+2 ;Used to force inverse/normal text
Width EQU OrMask+1 ;Holds date/time width in pixels
* grafPort record
Port EQU Width+2
PortInfo EQU Port ;portSCB
PortRect EQU Port+16
ClipRgn EQU Port+24
VisRgn EQU Port+28
* In order to make the program occupy less disk space
* all buffers are here at the end and only equates are
* included in the source file. When the program is
* run, the OMF loader will allocate the buffer
* as requested by the link file that is used to assemble
* this file. If using another assembler you may need
* to use DS $D2 here instead.